package com.ynet.middleground.approve.model;

import java.util.List;
import java.util.Optional;

import org.activiti.bpmn.model.BpmnModel;
import org.activiti.editor.constants.ModelDataJsonConstants;
import org.activiti.editor.language.json.converter.BpmnJsonConverter;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ModelQuery;
import org.apache.commons.lang3.StringUtils;
import org.dozer.Mapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.ynet.core.common.PagerInfo;
import com.ynet.core.exception.BusinessException;
import com.ynet.middleground.approve.bean.CreateProcessModelReq;
import com.ynet.middleground.approve.bean.ProcessModelReq;
import com.ynet.middleground.approve.bean.QueryModelReqPager;
import com.ynet.middleground.approve.dto.ProcessModelDTO;
import com.ynet.middleground.approve.utils.BeanUtil;

/**
 * @author liulx
 * @description 流程模型相关业务处理
 * @date 2019-10-20 11:42
 */
@Component
public class ProcessModelerModel {

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    private Mapper dozerBeanMapper;

    /**
     * 创建模型
     *
     * @param processModelReq
     *            流程模型创建请求对象: {模型名称,key,描述}
     */
    public String create(CreateProcessModelReq processModelReq) {
        Model queryModel =
            repositoryService.createModelQuery().modelKey(processModelReq.getKey()).latestVersion().singleResult();
        if (queryModel != null) {
            throw new BusinessException("该模型已经存在", "ECAP0300");
        }
        Model model = repositoryService.newModel();
        ObjectNode modelNode = objectMapper.createObjectNode();
        if (StringUtils.isBlank(processModelReq.getName())) {
            throw new BusinessException("流程模型名称不能为空", "ECAP0001");
        }
        if (StringUtils.isBlank(processModelReq.getKey())) {
            throw new BusinessException("流程key不能为空", "ECAP0001");
        }
        modelNode.put(ModelDataJsonConstants.MODEL_NAME, processModelReq.getName());
        modelNode.put(ModelDataJsonConstants.MODEL_DESCRIPTION,
            StringUtils.isBlank(processModelReq.getDesc()) ? "" : processModelReq.getDesc());
        modelNode.put(ModelDataJsonConstants.MODEL_REVISION, 1);
        model.setName(processModelReq.getName());
        model.setKey(processModelReq.getKey());
        model.setMetaInfo(modelNode.toString());
        model.setCategory(processModelReq.getCategory());
        repositoryService.saveModel(model);
        createObjectNode(model.getId());
        return model.getId();
    }

    /**
     * 创建模型时完善ModelEditorSource
     *
     * @param modelId
     *            模型ID
     */
    private void createObjectNode(String modelId) {
        ObjectNode editorNode = objectMapper.createObjectNode();
        editorNode.put("id", "canvas");
        editorNode.put("resourceId", "canvas");
        ObjectNode stencilSetNode = objectMapper.createObjectNode();
        stencilSetNode.put("namespace", "http://b3mn.org/stencilset/bpmn2.0#");
        // editorNode.put("stencilset", stencilSetNode);
        editorNode.set("stencilset", stencilSetNode);
        try {
            repositoryService.addModelEditorSource(modelId, editorNode.toString().getBytes("utf-8"));
        } catch (Exception e) {
            throw new BusinessException("创建模型时完善ModelEditorSource服务异常", "ECAP0000");
        }
    }

    /**
     * 发布模型
     *
     * @param processModelReq
     *            流程模型发布请求对象: {模型ID}
     */
    public void publish(ProcessModelReq processModelReq) {
        if (StringUtils.isBlank(processModelReq.getModelId())) {
            throw new BusinessException("模型ID不能为空", "ECAP0001");
        }
        String modelId = processModelReq.getModelId();
        Model modelData = repositoryService.getModel(modelId);
        Optional.ofNullable(modelData).orElseThrow(() -> new BusinessException("流程模型信息不存在", "ECAP0300"));
        byte[] bytes = repositoryService.getModelEditorSource(modelData.getId());
        if (bytes == null) {
            throw new BusinessException("部署的模型数据为空，请先设计流程并成功保存，再进行发布", "ECAP0301");
        }
        try {
            JsonNode modelNode = new ObjectMapper().readTree(bytes);
            BpmnModel model = new BpmnJsonConverter().convertToBpmnModel(modelNode);
            Deployment deployment = repositoryService.createDeployment().name(modelData.getName())
                .addBpmnModel(modelData.getKey() + ".bpmn20.xml", model).deploy();
            modelData.setDeploymentId(deployment.getId());
            repositoryService.saveModel(modelData);
        } catch (Exception e) {
            throw new BusinessException("设计模型图不正确，检查模型正确性", "ECAP0302");
        }
    }

    /**
     * 分页查询流程模型列表
     *
     * @param queryModelReqPager
     *            流程模型查询请求类: {模型名称, key}
     * @return 流程模型列表
     */
    public List<ProcessModelDTO> listModelByPage(QueryModelReqPager queryModelReqPager) {
        PagerInfo pagerInfo = queryModelReqPager.getPagerInfo();
        String name = queryModelReqPager.getName();
        ModelQuery query = repositoryService.createModelQuery();
        if (StringUtils.isNotBlank(name)) {
            query = query.modelNameLike("%" + name + "%");
        }
        if (StringUtils.isNotBlank(queryModelReqPager.getKey())) {
            query = query.modelKey(queryModelReqPager.getKey());
        }
        query = query.latestVersion().orderByCreateTime().desc();
        int start = (pagerInfo.getPageNum() - 1) * pagerInfo.getPageSize();
        // 流程定义列表
        List<Model> queryList = query.listPage(start, pagerInfo.getPageSize());
        List<ProcessModelDTO> resultList = BeanUtil.mapList(dozerBeanMapper, queryList, ProcessModelDTO.class);;
        pagerInfo.setTotal(query.count());
        return resultList;
    }

    /**
     * 根据模型ID删除模型
     * 
     * @param processModelReq
     *            删除模型请求对象: {模型ID}
     */
    public void removeModelByModelId(ProcessModelReq processModelReq) {
        if (StringUtils.isBlank(processModelReq.getModelId())) {
            throw new BusinessException("模型ID不能为空", "ECAP0001");
        }
        Model model = repositoryService.createModelQuery().modelId(processModelReq.getModelId()).singleResult();
        Optional.ofNullable(model).orElseThrow(() -> new BusinessException("流程模型信息不存在", "ECAP0300"));
        repositoryService.deleteModel(processModelReq.getModelId());
    }

}
